package com.kc.data.common.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.RequestMethod;
import com.ijpay.core.kit.AesUtil;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.core.utils.DateTimeZoneUtil;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxApiType;
import com.ijpay.wxpay.enums.WxDomain;
import com.ijpay.wxpay.model.v3.Amount;
import com.ijpay.wxpay.model.v3.SettleInfo;
import com.ijpay.wxpay.model.v3.UnifiedOrderModel;
import com.kc.data.common.base.BeanUtils;
import com.kc.data.common.base.StringUtils;
import com.kc.data.common.domain.model.wxPay.ApplymentsDTO;
import com.kc.data.common.domain.model.wxPay.SubOrderDTO;
import com.kc.data.common.domain.model.wxPay.WxPayV3Bean;
import com.kc.data.common.exception.CustomException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.gabin.tools.config.ProfitsSharingConfig;
import top.gabin.tools.request.ecommerce.applyments.ApplymentsModifySettlementRequest;
import top.gabin.tools.request.ecommerce.applyments.ApplymentsRequest;
import top.gabin.tools.request.ecommerce.fund.WithdrawForSubMchRequest;
import top.gabin.tools.request.ecommerce.profitsharing.ProfitSharingAddReceiverRequest;
import top.gabin.tools.request.ecommerce.profitsharing.ProfitSharingApplyRequest;
import top.gabin.tools.request.ecommerce.profitsharing.ProfitSharingFinishRequest;
import top.gabin.tools.request.ecommerce.profitsharing.ProfitSharingQueryApplyRequest;
import top.gabin.tools.request.ecommerce.refunds.RefundApplyRequest;
import top.gabin.tools.request.pay.combine.CombineTransactionsAppRequest;
import top.gabin.tools.response.ecommerce.amount.AmountOnlineOfSubMchResponse;
import top.gabin.tools.response.ecommerce.applyments.ApplymentsModifySettlementResponse;
import top.gabin.tools.response.ecommerce.applyments.ApplymentsResponse;
import top.gabin.tools.response.ecommerce.applyments.ApplymentsSettlementStatusResponse;
import top.gabin.tools.response.ecommerce.applyments.ApplymentsStatusResponse;
import top.gabin.tools.response.ecommerce.fund.WithdrawForSubMchResponse;
import top.gabin.tools.response.ecommerce.fund.WithdrawStatusForSubMchResponse;
import top.gabin.tools.response.ecommerce.profitsharing.ProfitSharingAddReceiverResponse;
import top.gabin.tools.response.ecommerce.profitsharing.ProfitSharingApplyResponse;
import top.gabin.tools.response.ecommerce.profitsharing.ProfitSharingFinishResponse;
import top.gabin.tools.response.ecommerce.profitsharing.ProfitSharingQueryApplyResponse;
import top.gabin.tools.response.ecommerce.refunds.RefundApplyResponse;
import top.gabin.tools.response.pay.combine.CombineTransactionsStatusResponse;
import top.gabin.tools.service.ProfitsSharingService;
import top.gabin.tools.service.ProfitsSharingServiceImpl;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.*;

/**
 * TODO
 *
 * @author zhangshuo
 * @version 1.0
 * @date 2020/12/25
 */
@Slf4j
@Service
public class WxPartnerService {
    @Autowired
    WxPayV3Bean wxPayV3Bean;


    ProfitsSharingService profitsSharingService;

    X509Certificate certificate;

    String serialNo;

    public ProfitsSharingService before() {

        try {
            ProfitsSharingConfig config = new ProfitsSharingConfig();
            config.setMchId(wxPayV3Bean.getMchId());
            config.setApiKey(wxPayV3Bean.getApiKey3());
            certificate = PemUtil.loadCertificate(new FileInputStream(wxPayV3Bean.getCertPath()));
            String serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
            config.setMchSerialNo(serialNo);
            config.setPrivateKey(PemUtil.loadPrivateKey(new FileInputStream(wxPayV3Bean.getKeyPath())));
            profitsSharingService = new ProfitsSharingServiceImpl(config, null);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        return profitsSharingService;
    }

    public Map<String, Object> applyments(String outRequestNo, ApplymentsDTO applymentsDTO) {
        ApplymentsRequest applymentsRequest = new ApplymentsRequest();
        applymentsRequest.setOutRequestNo(outRequestNo);
        applymentsRequest.setOrganizationType(applymentsDTO.getOrganizationType());
        applymentsRequest.setMerchantShortname(applymentsDTO.getMerchantShortname());
        //银行账户
        applymentsRequest.setNeedAccountInfo(true);
        ApplymentsRequest.AccountInfo accountInfo = new ApplymentsRequest.AccountInfo();
        accountInfo.setBankAccountType(applymentsDTO.getBankAccountType());
        accountInfo.setAccountName(applymentsDTO.getAccountName());
        accountInfo.setAccountBank(applymentsDTO.getAccountBank());
        accountInfo.setBankAddressCode(applymentsDTO.getBankAddressCode());
        accountInfo.setAccountNumber(applymentsDTO.getAccountNumber());
        accountInfo.setBankName(applymentsDTO.getBankName());
        applymentsRequest.setAccountInfo(accountInfo);
        ApplymentsRequest.ContactInfo contactInfo = new ApplymentsRequest.ContactInfo();
        contactInfo.setContactType(applymentsDTO.getContactType());
        contactInfo.setContactName(applymentsDTO.getContactName());
        contactInfo.setContactIdCardNumber(applymentsDTO.getContactIdCardNumber());
        contactInfo.setMobilePhone(applymentsDTO.getMobilePhone());
        if (StringUtils.isNotBlank(applymentsDTO.getContactEmail())) {
            contactInfo.setContactEmail(applymentsDTO.getContactEmail());
        }
        applymentsRequest.setContactInfo(contactInfo);
        ApplymentsRequest.SalesSceneInfo salesSceneInfo = new ApplymentsRequest.SalesSceneInfo();
        salesSceneInfo.setStoreName(applymentsDTO.getStoreName());
        salesSceneInfo.setStoreUrl("http://www.qq.com");
        applymentsRequest.setSalesSceneInfo(salesSceneInfo);
        ApplymentsRequest.IdCardInfo idCardInfo = new ApplymentsRequest.IdCardInfo();
        idCardInfo.setIdCardName(applymentsDTO.getIdCardName());
        idCardInfo.setIdCardNumber(applymentsDTO.getIdCardNumber());
        idCardInfo.setIdCardValidTime(applymentsDTO.getIdCardValidTime());
        idCardInfo.setIdCardCopy(applymentsDTO.getIdCardCopy());
        idCardInfo.setIdCardNational(applymentsDTO.getIdCardNational());
        applymentsRequest.setIdCardInfo(idCardInfo);
        ApplymentsRequest.BusinessLicenseInfo businessLicenseInfo = new ApplymentsRequest.BusinessLicenseInfo();
        businessLicenseInfo.setMerchantName(applymentsDTO.getMerchantName());
        businessLicenseInfo.setBusinessLicenseNumber(applymentsDTO.getBusinessLicenseNumber());
        businessLicenseInfo.setLegalPerson(applymentsDTO.getLegalPerson());
        businessLicenseInfo.setBusinessLicenseCopy(applymentsDTO.getBusinessLicenseCopy());
        if (businessLicenseInfo != null) {
            applymentsRequest.setBusinessLicenseInfo(businessLicenseInfo);
        }
        before();
        Optional<ApplymentsResponse> applymentsStatusResponse = profitsSharingService.applyments(applymentsRequest);
        System.out.println(BeanUtils.beanToMap(applymentsStatusResponse).toString());
        applymentsStatusResponse.ifPresent(response -> log.info("applyments==" + JsonUtils.bean2Json(response)));
        return JSON.parseObject(JsonUtils.bean2Json(applymentsStatusResponse.get()), Map.class);
    }

    /**
     * 查询二级商户入驻结果
     *
     * @param applymentId
     * @return
     */
    public Map<String, Object> queryApplyments(String applymentId) {
        before();
        Optional<ApplymentsStatusResponse> applymentsStatusResponse = profitsSharingService.queryApplymentsStatus(applymentId);
        applymentsStatusResponse.ifPresent(response -> log.info(JsonUtils.bean2Json(response)));
        return JSON.parseObject(JsonUtils.bean2Json(applymentsStatusResponse.get()), Map.class);
    }

    /**
     * 修改结算账户
     *
     * @param accountNumber   银行卡号
     * @param accountBank     开户银行
     * @param accountType     账户类型 ACCOUNT_TYPE_BUSINESS：对公银行账户 ACCOUNT_TYPE_PRIVATE：经营者个人银行卡
     * @param bankAddressCode 开户银行省市编码
     * @param subMchid        特约商户号
     * @return
     */
    public Map<String, Object> modifySettlement(String accountNumber, String accountBank, String accountType, String bankAddressCode, String subMchid) {
        before();
        ApplymentsModifySettlementRequest request = new ApplymentsModifySettlementRequest();
        request = new ApplymentsModifySettlementRequest();
        request.setAccountNumber(accountNumber);
        request.setAccountBank(accountBank);
        request.setAccountType(accountType);
        request.setBankAddressCode(bankAddressCode);
        request.setSubMchid(subMchid);
        Optional<ApplymentsModifySettlementResponse> applymentsModifySettlementResponse = profitsSharingService.modifySettlement(request);
        applymentsModifySettlementResponse.ifPresent(response -> log.info(JsonUtils.bean2Json(response)));
        return JSON.parseObject(JsonUtils.bean2Json(applymentsModifySettlementResponse.get()), Map.class);
    }

    /**
     * 查询结算账户
     *
     * @param subMchid 特约商户号
     * @return
     */
    public Map<String, Object> querySettlement(String subMchid) {
        before();
        Optional<ApplymentsSettlementStatusResponse> applymentsSettlementStatusResponse = profitsSharingService.querySettlement(subMchid);
        applymentsSettlementStatusResponse.ifPresent(response -> log.info(JsonUtils.bean2Json(response)));
        return JSON.parseObject(JsonUtils.bean2Json(applymentsSettlementStatusResponse.get()), Map.class);
    }

    /**
     * 查询订单
     *
     * @param subMchid   二级商户号
     * @param outTradeNo 商户订单号
     * @return
     */
    public Map<String, Object> queryTransactions(String subMchid, String outTradeNo) {
        before();
        Map<String, String> params = new HashMap<>();
        params.put("sp_mchid", wxPayV3Bean.getMchId());
        params.put("sub_mchid", subMchid);
        try {

            IJPayHttpResponse response = WxPayApi.v3(RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    String.format(WxApiType.PARTNER_ORDER_QUERY_BY_NO.toString(), outTradeNo),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    null,
                    wxPayV3Bean.getKeyPath(),
                    params);
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Bean.getPlatformCertPath());
            log.info("verifySignature: {}", verifySignature);
            log.info("统一下单响应 {}", response);
            if (verifySignature) {
                return JSON.parseObject(response.getBody(), Map.class);
            } else {
                throw new CustomException("订单查询失败");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * 统一下单
     *
     * @param subMchid      二级商户的商户号，微信支付生成并下发
     * @param outTradeNo    商户系统内部订单号，只能是数字、大小写字母_-*且在同一个商户号下唯一，详见【商户订单号】。特殊规则：最小字符长度为6
     * @param totalFee      订单总金额，单位为分
     * @param description   商品描述
     * @param notifyUrl     通知URL必须为直接可访问的URL，不允许携带查询串
     * @param attach        附加数据，在查询API和支付通知中原样返回，可作为自定义参数使用
     * @param profitSharing 是否指定分账，枚举值true：是 false：否
     * @return
     */
    public Map<String, String> appPay(String subMchid, String outTradeNo, Integer totalFee, String description, String notifyUrl, String attach, Boolean profitSharing) {
        try {
            String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
            UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                    .setSp_appid(wxPayV3Bean.getAppId())
                    .setSp_mchid(wxPayV3Bean.getMchId())
                    .setSub_mchid(subMchid)
                    .setDescription(description)
                    .setOut_trade_no(outTradeNo)
                    .setTime_expire(timeExpire)
                    .setAttach(attach)
                    .setNotify_url(wxPayV3Bean.getDomain().concat(notifyUrl))
                    .setAmount(new Amount().setTotal(totalFee))
                    .setSettle_info(new SettleInfo().setProfit_sharing(profitSharing));

            log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethod.POST,
                    WxDomain.CHINA.toString(),
                    WxApiType.PARTNER_APP_PAY.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    null,
                    wxPayV3Bean.getKeyPath(),
                    JSONUtil.toJsonStr(unifiedOrderModel)
            );
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Bean.getPlatformCertPath());
            log.info("verifySignature: {}", verifySignature);
            log.info("统一下单响应 {}", response);

            if (verifySignature) {
                String body = response.getBody();
                JSONObject jsonObject = JSONUtil.parseObj(body);
                String prepayId = jsonObject.getStr("prepay_id");
                profitsSharingService = before();
//                Map<String, String> map = profitsSharingService.getAppPayParams(prepayId, wxPayV3Bean.getAppId());
                Map<String, String> map = WxPayKit.appCreateSign(wxPayV3Bean.getAppId(), wxPayV3Bean.getMchId(), prepayId, wxPayV3Bean.getKeyPath());
                log.info("唤起支付参数:{}", map);
                return map;
            }
            //todo 验签失败
            return new HashMap<>();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 合单支付
     *
     * @param combineOutTradeNo 合单单号
     * @param notifyUrl         支付回调地址
     * @param subOrderDTOS      子单
     * @return
     */
    public Map<String, String> combineTransactions(String combineOutTradeNo, String notifyUrl, List<SubOrderDTO> subOrderDTOS) {
        before();
        Optional<String> combineResponse = profitsSharingService.combineTransactions(getCombineTransactionsRequest(combineOutTradeNo, notifyUrl, subOrderDTOS));
        combineResponse.ifPresent(response -> log.info(JsonUtils.bean2Json(response)));
        Map<String, String> map = null;
        String prepayId = combineResponse.get();
        try {
            map = WxPayKit.appCreateSign(wxPayV3Bean.getAppId(), wxPayV3Bean.getMchId(), prepayId, wxPayV3Bean.getKeyPath());
//            map = profitsSharingService.getAppPayParams(JsonUtils.bean2Json(combineResponse.get()), wxPayV3Bean.getAppId());
            log.info("唤起支付参数:{}", map);
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private CombineTransactionsAppRequest getCombineTransactionsRequest(String combineOutTradeNo, String notifyUrl, List<SubOrderDTO> subOrderDTOS) {
        CombineTransactionsAppRequest request = new CombineTransactionsAppRequest();
        request.setCombineAppid(wxPayV3Bean.getAppId());
        request.setCombineMchid(wxPayV3Bean.getMchId());
        request.setNotifyUrl(wxPayV3Bean.getDomain().concat(notifyUrl));

        Date timeout = DateUtils.addMinutes(new Date(), 30);
        request.setTimeExpire(formatRFC3339ToString(timeout));

        List<CombineTransactionsAppRequest.SubOrders> subOrdersList = new ArrayList<>();
        for (SubOrderDTO subOrderDTO : subOrderDTOS) {
            CombineTransactionsAppRequest.SubOrders subOrders = new CombineTransactionsAppRequest.SubOrders();
            CombineTransactionsAppRequest.Amount amount = new CombineTransactionsAppRequest.Amount();
            amount.setCurrency("CNY");
            amount.setTotalAmount(subOrderDTO.getTotalAmount());
            subOrders.setAmount(amount);
            subOrders.setOutTradeNo(subOrderDTO.getOutTradeNo());
            if (StringUtils.isNotBlank(subOrderDTO.getAttach())) {
                subOrders.setAttach(subOrderDTO.getAttach());
            }
            subOrders.setMchid(wxPayV3Bean.getMchId());
            subOrders.setSubMchid(subOrderDTO.getSubMchid());

            CombineTransactionsAppRequest.SettleInfo settleInfo = new CombineTransactionsAppRequest.SettleInfo();
            settleInfo.setProfitSharing(subOrderDTO.getProfitSharing());
            subOrders.setSettleInfo(settleInfo);
            subOrders.setDescription(subOrderDTO.getDescription());
            subOrdersList.add(subOrders);
        }

        request.setSubOrders(subOrdersList);

//        CombineTransactionsAppRequest.SceneInfo sceneInfo = new CombineTransactionsAppRequest.SceneInfo();
//        request.setSceneInfo(sceneInfo);
//        sceneInfo.setPayerClientIp("127.0.0.1");

//        CombineTransactionsAppRequest.CombinePayerInfo combinePayerInfo = new CombineTransactionsAppRequest.CombinePayerInfo();
//        combinePayerInfo.setOpenid("ooVCYuPHSANi2SFlVcvoVnlbpnoI");
//        request.setCombinePayerInfo(combinePayerInfo);

        request.setCombineOutTradeNo(combineOutTradeNo);
        return request;
    }

    /**
     * 合单查询订单
     *
     * @param outTradeNo 合单商户订单号
     * @return
     */
    public Map<String, Object> queryTransactions(String outTradeNo) {
        before();
        Optional<CombineTransactionsStatusResponse> combineTransactionsStatusResponse = profitsSharingService.combineTransactionsStatus(outTradeNo);
        combineTransactionsStatusResponse.ifPresent(response -> log.info(JsonUtils.bean2Json(response)));
        return JSON.parseObject(JsonUtils.bean2Json(combineTransactionsStatusResponse.get()), Map.class);
    }

    /**
     * 获取证书序列号
     *
     * @return
     */
    private String getSerialNumber() {
        if (StringUtils.isEmpty(serialNo)) {
            // 获取证书序列号
            X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wxPayV3Bean.getCertPath()));
            serialNo = certificate.getSerialNumber().toString(16).toUpperCase();

//            System.out.println("输出证书信息:\n" + certificate.toString());
//            // 输出关键信息，截取部分并进行标记
//            System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16));
//            System.out.println("版本号:" + certificate.getVersion());
//            System.out.println("签发者：" + certificate.getIssuerDN());
//            System.out.println("有效起始日期：" + certificate.getNotBefore());
//            System.out.println("有效终止日期：" + certificate.getNotAfter());
//            System.out.println("主体名：" + certificate.getSubjectDN());
//            System.out.println("签名算法：" + certificate.getSigAlgName());
//            System.out.println("签名：" + certificate.getSignature().toString());
        }
        System.out.println("serialNo:" + serialNo);
        return serialNo;
    }

    /**
     * 更新平台证书
     *
     * @param associatedData
     * @param nonce
     * @param cipherText
     * @param certPath
     * @return
     */
    private String savePlatformCert(String associatedData, String nonce, String cipherText, String certPath) {
        try {
            AesUtil aesUtil = new AesUtil(wxPayV3Bean.getApiKey3().getBytes(StandardCharsets.UTF_8));
            // 平台证书密文解密
            // encrypt_certificate 中的  associated_data nonce  ciphertext
            String publicKey = aesUtil.decryptToString(
                    associatedData.getBytes(StandardCharsets.UTF_8),
                    nonce.getBytes(StandardCharsets.UTF_8),
                    cipherText
            );
            // 保存证书
            FileWriter writer = new FileWriter(certPath);
            writer.write(publicKey);
            writer.close();
            // 获取平台证书序列号
            X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
            return certificate.getSerialNumber().toString(16).toUpperCase();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 获取平台证书列表
     *
     * @return
     */
    public String getCertificates() {
        // 获取平台证书列表
        try {
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    WxApiType.GET_CERTIFICATES.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    null,
                    wxPayV3Bean.getKeyPath(),
                    ""
            );

            String timestamp = response.getHeader("Wechatpay-Timestamp");
            String nonceStr = response.getHeader("Wechatpay-Nonce");
            String serialNumber = response.getHeader("Wechatpay-Serial");
            String signature = response.getHeader("Wechatpay-Signature");

            String body = response.getBody();
            int status = response.getStatus();

            log.info("serialNumber: {}", serialNumber);
            log.info("status: {}", status);
            log.info("body: {}", body);
            int isOk = 200;
            if (status == isOk) {
                JSONObject jsonObject = JSONUtil.parseObj(body);
                JSONArray dataArray = jsonObject.getJSONArray("data");
                // 默认认为只有一个平台证书
                JSONObject encryptObject = dataArray.getJSONObject(0);
                JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
                String associatedData = encryptCertificate.getStr("associated_data");
                String cipherText = encryptCertificate.getStr("ciphertext");
                String nonce = encryptCertificate.getStr("nonce");
                String serialNo = encryptObject.getStr("serial_no");
                final String platSerialNo = savePlatformCert(associatedData, nonce, cipherText, wxPayV3Bean.getPlatformCertPath());
                log.info("平台证书序列号: {} serialNo: {}", platSerialNo, serialNo);
            }
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Bean.getPlatformCertPath());
            System.out.println("verifySignature:" + verifySignature);
            return body;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public String v3Upload(String filePath) {
        // v3 接口上传文件
        try {
            File file = FileUtil.newFile(filePath);
            String sha256 = SecureUtil.sha256(file);

            HashMap<Object, Object> map = new HashMap<>();
            map.put("filename", file.getName());
            map.put("sha256", sha256);
            String body = JSONUtil.toJsonStr(map);

            System.out.println(body);

            IJPayHttpResponse result = WxPayApi.v3(
                    WxDomain.CHINA.toString(),
                    WxApiType.MERCHANT_UPLOAD_MEDIA.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    null,
                    wxPayV3Bean.getKeyPath(),
                    body,
                    file
            );
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(result, wxPayV3Bean.getPlatformCertPath());
            System.out.println("verifySignature:" + verifySignature);
            System.out.println(result);
            return JSONUtil.toJsonStr(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public Map<String, Object> payNotify(HttpServletRequest request) {
        try {
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String serialNo = request.getHeader("Wechatpay-Serial");
            String signature = request.getHeader("Wechatpay-Signature");

            log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
            String result = HttpKit.readData(request);
            log.info("支付通知密文 {}", result);

            // 需要通过证书序列号查找对应的证书，verifyNotify 中有验证证书的序列号
            String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
                    wxPayV3Bean.getApiKey3(), wxPayV3Bean.getPlatformCertPath());

            log.info("支付通知明文 {}", plainText);

            Map<String, Object> resultMap = new HashMap<>();
            resultMap = JSON.parseObject(result, Map.class);
            Map<String, Object> plainMap = new HashMap<>();
            plainMap = JSON.parseObject(plainText, Map.class);
            plainMap.put("event_type", resultMap.get("event_type"));
            return plainMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 发起退款
     *
     * @param subMchid    二级商户号
     * @param total       原订单金额
     * @param refund      退款金额
     * @param outTradeNo  原支付交易对应的商户订单号
     * @param outRefundNo 商户系统内部的退款单号，商户系统内部唯一，只能是数字、大小写字母_-|*@，同一退款单号多次请求只退一笔
     * @param notifyUrl   异步接收微信支付退款结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数
     * @return
     */
    public Map<String, Object> refund(String subMchid, Integer total, Integer refund, String outTradeNo, String outRefundNo, String notifyUrl) {
        log.info("subMchid={},total={},refund={},outTradeNo={},outRefundNo={},notifyUrl={}");
        before();
        RefundApplyRequest refundApplyRequest = new RefundApplyRequest();
        RefundApplyRequest.Amount amount = new RefundApplyRequest.Amount();
        amount.setRefund(refund);
        amount.setTotal(total);
        amount.setCurrency("CNY");
        refundApplyRequest.setAmount(amount);
        refundApplyRequest.setOutTradeNo(outTradeNo);
        refundApplyRequest.setSubMchid(subMchid);
        refundApplyRequest.setSpAppid(wxPayV3Bean.getAppId());
        refundApplyRequest.setOutRefundNo(outRefundNo);
        refundApplyRequest.setNotifyUrl(wxPayV3Bean.getDomain() + notifyUrl);
        Optional<RefundApplyResponse> refundApplyResponse = profitsSharingService.refundApply(refundApplyRequest);
        refundApplyResponse.ifPresent(response -> log.info(JsonUtils.bean2Json(response)));
        return JSON.parseObject(JsonUtils.bean2Json(refundApplyResponse.get()), Map.class);
    }


    /**
     * 退款结果回调
     *
     * @param request
     * @return
     */
    public Map<String, Object> refundNotify(HttpServletRequest request) {
        try {
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String serialNo = request.getHeader("Wechatpay-Serial");
            String signature = request.getHeader("Wechatpay-Signature");

            log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
            String result = HttpKit.readData(request);
            log.info("退款通知密文 {}", result);

            // 需要通过证书序列号查找对应的证书，verifyNotify 中有验证证书的序列号
            String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
                    wxPayV3Bean.getApiKey3(), wxPayV3Bean.getPlatformCertPath());

            log.info("退款通知明文 {}", plainText);

//            return JSON.parseObject(plainText, Map.class);


            Map<String, Object> resultMap = new HashMap<>();
            resultMap = JSON.parseObject(result, Map.class);
            Map<String, Object> plainMap = new HashMap<>();
            plainMap = JSON.parseObject(plainText, Map.class);
            plainMap.put("event_type", resultMap.get("event_type"));
            return plainMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 请求分账
     *
     * @param receivers     分账接收方列表，支持设置出资商户作为分账接收方，单次分账最多可有5个分账接收方
     * @param subMchid      分账出资的电商平台二级商户，填写微信支付分配的商户号。
     * @param transactionId 微信支付订单号
     * @param outOrderNo    商户系统内部的分账单号，在商户系统内部唯一（单次分账、多次分账、完结分账应使用不同的商户分账单号），同一分账单号多次请求等同一次。
     */
    public Map<String, Object> profitsSharing(List<ProfitSharingApplyRequest.Receivers> receivers, String subMchid, String transactionId, String outOrderNo) {
        before();
        ProfitSharingApplyRequest sharingApplyRequest = new ProfitSharingApplyRequest();
        sharingApplyRequest.setAppid(wxPayV3Bean.getAppId());
        sharingApplyRequest.setReceivers(receivers);
        sharingApplyRequest.setSubMchid(subMchid);
        sharingApplyRequest.setTransactionId(transactionId);
        sharingApplyRequest.setOutOrderNo(outOrderNo);
        sharingApplyRequest.setFinish(true);
        try {
            System.out.println("================" + new ObjectMapper().writeValueAsString(sharingApplyRequest));
        } catch (IOException e) {
            e.printStackTrace();
        }
        Optional<ProfitSharingApplyResponse> profitSharingApplyResponse = profitsSharingService.applyProfitSharing(sharingApplyRequest);
        profitSharingApplyResponse.ifPresent(response -> log.info(JsonUtils.bean2Json(response)));
        return JSON.parseObject(JsonUtils.bean2Json(profitSharingApplyResponse.get()), Map.class);
    }

    /**
     * 完结分帐
     *
     * @param subMchid      分账出资的电商平台二级商户，填写微信支付分配的商户号。
     * @param transactionId 微信支付订单号
     * @param outOrderNo    商户系统内部的分账单号，在商户系统内部唯一（单次分账、多次分账、完结分账应使用不同的商户分账单号），同一分账单号多次请求等同一次。
     * @param description   分账的原因描述，分账账单中需要体现
     * @return
     */
    public Map<String, Object> finishProfitsSharing(String subMchid, String transactionId, String outOrderNo, String description) {
        before();
        ProfitSharingFinishRequest profitSharingFinishRequest = new ProfitSharingFinishRequest();
        profitSharingFinishRequest.setSubMchid(subMchid);
        profitSharingFinishRequest.setTransactionId(transactionId);
        profitSharingFinishRequest.setOutOrderNo(outOrderNo);
        profitSharingFinishRequest.setDescription(description);
        Optional<ProfitSharingFinishResponse> profitSharingFinishResponse = profitsSharingService.finishProfitSharing(profitSharingFinishRequest);
        profitSharingFinishResponse.ifPresent(response -> log.info(JsonUtils.bean2Json(response)));
        return JSON.parseObject(JsonUtils.bean2Json(profitSharingFinishResponse.get()), Map.class);
    }

    /**
     * 查询分账结果
     *
     * @param subMchid      分账出资的电商平台二级商户，填写微信支付分配的商户号
     * @param transactionId 微信支付订单号。
     * @param outOrderNo    商户系统内部的分账单号，在商户系统内部唯一（单次分账、多次分账、完结分账应使用不同的商户分账单号），同一分账单号多次请求等同一次。
     * @return
     */
    public Map<String, Object> queryProfitSharingStatus(String subMchid, String transactionId, String outOrderNo) {
        before();
        ProfitSharingQueryApplyRequest request = new ProfitSharingQueryApplyRequest();
        request.setSubMchid(subMchid);
        request.setTransactionId(transactionId);
        request.setOutOrderNo(outOrderNo);
        Optional<ProfitSharingQueryApplyResponse> profitSharingQueryApplyResponse = profitsSharingService.queryProfitSharingStatus(request);
        profitSharingQueryApplyResponse.ifPresent(response -> log.info(JsonUtils.bean2Json(response)));
        return JSON.parseObject(JsonUtils.bean2Json(profitSharingQueryApplyResponse.get()), Map.class);
    }

    /**
     * 查询二级商户实时余额
     *
     * @param subMchid 电商平台二级商户号，由微信支付生成并下发。
     * @return
     */
    public Map<String, Object> queryOnlineBalance(String subMchid) {
        before();
        Optional<AmountOnlineOfSubMchResponse> response = profitsSharingService.queryOnlineAmount(subMchid);
        response.ifPresent(amountOnlineOfSubMchResponse -> log.info(JsonUtils.bean2Json(amountOnlineOfSubMchResponse)));
        return JSON.parseObject(JsonUtils.bean2Json(response.get()), Map.class);
    }

    /**
     * 二级商户余额提现
     *
     * @param amount     单位：分，金额不能超过8亿元
     * @param subMchid   电商平台二级商户号，由微信支付生成并下发
     * @param outOrderNo 商户提现单号，由商户自定义生成，必须是字母数字。
     * @param remark     商户对提现单的备注，商户自定义字段
     * @return
     */
    public Map<String, Object> withdraw(Integer amount, String subMchid, String outOrderNo, String remark) {
        before();
        WithdrawForSubMchRequest request = new WithdrawForSubMchRequest();
        request.setAmount(amount);
        request.setOutRequestNo(outOrderNo);
        request.setSubMchid(subMchid);
        request.setRemark(remark);
        Optional<WithdrawForSubMchResponse> withdraw = profitsSharingService.withdraw(request);
        withdraw.ifPresent(response -> log.info(JsonUtils.bean2Json(response)));
        return JSON.parseObject(JsonUtils.bean2Json(withdraw.get()), Map.class);
    }

    /**
     * 二级商户查询提现状态
     *
     * @param subMchid   电商平台二级商户号，由微信支付生成并下发
     * @param withdrawId 电商平台提交二级商户提现申请后，由微信支付返回的申请单号，作为查询申请状态的唯一标识。
     * @return
     */
    public Map<String, Object> queryWithdraw(String subMchid, String withdrawId) {
        before();
        Optional<WithdrawStatusForSubMchResponse> responseOptional = profitsSharingService.queryWithdrawStatus(subMchid, withdrawId);
        responseOptional.ifPresent(response -> log.info(JsonUtils.bean2Json(response)));
        return JSON.parseObject(JsonUtils.bean2Json(responseOptional.get()), Map.class);
    }


    /**
     * 响应微信通知
     *
     * @param response
     */
    public void retrunSuccess(HttpServletResponse response) {
        try {
            Map<String, String> map = new HashMap<>(12);
            response.setStatus(200);
            map.put("code", "SUCCESS");
            map.put("message", "SUCCESS");
            response.setHeader("Content-type", ContentType.JSON.toString());
            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
            response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public Map<String, Object> addShareProfitReceivers(ProfitSharingAddReceiverRequest receiverRequest) {
        if (receiverRequest != null) {
            before();
            receiverRequest.setAppid(wxPayV3Bean.getAppId());
            Optional<ProfitSharingAddReceiverResponse> responseOptional = profitsSharingService.addReceiver(receiverRequest);
            responseOptional.ifPresent(response -> log.info(">>>添加分账接收方微信返回信息：" + JsonUtils.bean2Json(response)));
            return JSON.parseObject(JsonUtils.bean2Json(responseOptional.get()), Map.class);
        }
        throw new CustomException("请求参数不能为空");
    }

    private String formatRFC3339ToString(Date date) {
        java.text.DateFormat dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", Locale.US);
        return dateFormat.format(date);
    }
}
